'''Test module for localvalidator'''

import unittest
from localvalidator import *

class LocalValidatorTest(unittest.TestCase):
    
    def test_valid_condition(self):
        message = 'Number must be zero or positive'        
        def valid_condition(number):
            (LocalValidationContext()
                .add_valid_condition(number >= 0, message)
                .validate())            
        valid_condition(10)
        valid_condition(0)
        self.verify_exception(message, valid_condition, -1)
        
    def test_invalid_condition(self):
        message = 'Number can not be negative'
        def invalid_condition(number):
            (LocalValidationContext()
                .add_invalid_condition(number < 0, message)
                .validate())
        invalid_condition(10)
        invalid_condition(0)
        self.verify_exception(message, invalid_condition, -1)
        
            
    def test_not_none_checking(self):
        message = 'Value cannot be none'
        def not_none_checking(value):
            (LocalValidationContext()
                .add_not_none_checking(value, message)
                .validate())
        not_none_checking("I'm not a None")
        not_none_checking(42)
        self.verify_exception(message, not_none_checking, None)
        
    def test_none_checking(self):
        message = 'Value must be none'
        def none_checking(value):
            (LocalValidationContext()
                .add_none_checking(value, message)
                .validate())
        none_checking(None)
        self.verify_exception(message, none_checking, 0)
        self.verify_exception(message, none_checking, "I'm not None!")
        
    def test_non_empty_checking(self):
        message = 'Value cannot be empty'
        def non_empty_checking(object, _consider_spaces=True):
            (LocalValidationContext()
                 .add_non_empty_checking(object, message, consider_spaces=_consider_spaces)
                 .validate())
  
        non_empty_checking("I'm not empty")
        non_empty_checking([1, 2, 3])
        non_empty_checking({'a': 97, 'b': 98, 'c': 99})
        non_empty_checking((1,))
        non_empty_checking('     ')

        self.verify_exception(message, non_empty_checking, '')
        self.verify_exception(message, non_empty_checking, '       ', False)
        self.verify_exception(message, non_empty_checking, [])
        self.verify_exception(message, non_empty_checking, {})
        self.verify_exception(message, non_empty_checking, ())
        self.verify_exception(message, non_empty_checking, None)
        
    def test_equals_checking(self):
        message = 'The values must be equal'
        def equals_checking(object1, object2):
            (LocalValidationContext()
                .add_equals_checking(object1, object2, message)
                .validate())
        equals_checking("I'm equal", "I'm equal")
        equals_checking(1, 1)
        equals_checking(None, None)
        equals_checking([1], [1])
        equals_checking((1,), (1,))
        equals_checking({'1': 1}, {'1': 1})
        self.verify_exception(message, equals_checking, 1, 2)
        self.verify_exception(message, equals_checking, '1', '2')
        self.verify_exception(message, equals_checking, [1], [2])
        self.verify_exception(message, equals_checking, (1,), (2,))
        self.verify_exception(message, equals_checking, {1: 1}, {2: 1})
        self.verify_exception(message, equals_checking, None, '')
        self.verify_exception(message, equals_checking, None, 0)
        
    def test_not_equals_checking(self):
        message = 'The values cannot be equal'
        def not_equals_checking(object1, object2):
            (LocalValidationContext()
                .add_not_equals_checking(object1, object2, message)
                .validate())
        not_equals_checking(1, 2)
        not_equals_checking('1', '2')
        not_equals_checking([1], [2])
        not_equals_checking((1,), (2,))
        not_equals_checking({1: 1}, {2: 1})
        not_equals_checking(None, '')
        not_equals_checking(None, 0)
        self.verify_exception(message, not_equals_checking, "I'm equal", "I'm equal")
        self.verify_exception(message, not_equals_checking, 1, 1)
        self.verify_exception(message, not_equals_checking, None, None)
        self.verify_exception(message, not_equals_checking, [1], [1])
        self.verify_exception(message, not_equals_checking, (1,), (1,))
        self.verify_exception(message, not_equals_checking, {'1': 1}, {'1': 1})
        
    def test_custom_validation(self):
        
        class PalindromeVerifier:
            def __init__(self, string):
                self.string = string
            def __reverse(self, string):
                result = ''
                for ch in string:
                    result = ch + result
                return result
            def is_palindrome(self):
                return self.string == self.__reverse(self.string)
            def validate(self):
                return self.is_palindrome()
        a_palindrome = 'socorrammesubinoonibusemmarrocos'
        self.assertTrue(PalindromeVerifier(a_palindrome).is_palindrome()) # testing test fixture class
            
        message = 'The string must be a palindrome'
        def custom_validation(string):
            (LocalValidationContext()
                .add_custom_validation(PalindromeVerifier(string), message)
                .validate())
        custom_validation(a_palindrome)
        self.verify_exception(message, custom_validation, "I'm not a palindrome")
        
    def test_regex_checking(self):
        message = 'Value must be a valid e-mail address'
        def regex_checking(string, regex):
            (LocalValidationContext()
                .add_matching_checking(string, regex, message)
                .validate())
        mail_regex = r'\w+\@\w+\.\w+' # yes, it's very simplistic, but it isn't the point here
        regex_checking('rmanhaes@gmail.com', mail_regex)
        self.verify_exception(message, regex_checking, 'r manhaes@gmail.com', mail_regex)
        
    def test_validation(self):
        try:
            LocalValidationContext([LocalValidationCase(True, None), 
                                    LocalValidationCase(False, None)]).validate()
            self.fail('An exception must be thrown')
        except ValidationError, e:
            self.assertEquals('Validation error [2]', e.message)
            
    def test_another_exception_class(self):
        class MyTestException(Exception): 
            pass
        try:        
            (LocalValidationContext()
                .add_valid_condition(condition=False, exception_class=MyTestException)
                .validate())
            self.fail("An exception should have been thrown")
        except MyTestException:
            pass
        except:
            self.fail("An unexpected exception was thrown")     
        
    def verify_exception(self, expected_message, function, *params):
        actual_message = None
        try:
            function(*params)
            self.fail("Exception not thrown")
        except ValidationError, e:
            actual_message = e.message
        self.assertEquals(expected_message, actual_message)
        
if __name__ == '__main__':
    unittest.main()